package com.ala4.oxcafe.controller;

import com.ala4.oxcafe.boot.annotation.Permission;
import com.ala4.oxcafe.boot.page.PageDomain;
import com.ala4.oxcafe.boot.page.PageResult;
import com.ala4.oxcafe.common.utils.JSONUtil;
import com.ala4.oxcafe.common.utils.OssClientUtil;
import com.ala4.oxcafe.domain.R;
import com.ala4.oxcafe.entity.Attachment;
import com.ala4.oxcafe.enums.StorageTypeEnum;
import com.ala4.oxcafe.properties.FileProperties;
import com.ala4.oxcafe.service.AttachmentService;
import com.aliyun.oss.dto.OSSPutDTO;
import com.aliyun.oss.dto.OSSUrlDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.repository.query.Param;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.Base64;
import java.util.List;
import java.util.Map;

/**
 * 附件管理
 *
 * @author PING
 * @date 2024/1/10 21:52
 */
@Slf4j
@RestController
@RequestMapping("/v1/attachment")
@AllArgsConstructor
@Tag(name = "附件管理", description = "用于管理附件上传")
public class AttachmentController {

    private final AttachmentService attachmentService;

    private final FileProperties fileProperties;

    @Operation(summary = "分页查询附件")
    @GetMapping("/page")
    public R<PageResult<Attachment>> page(PageDomain pageDomain, Attachment attachment) {
        return R.ok(PageResult.iPage(attachmentService.attachmentPage(pageDomain, attachment)));
    }

    @Operation(summary = "列表查询附件")
    @GetMapping("/list")
    public R<List<Attachment>> list(Attachment attachment) {
        return R.ok(attachmentService.attachmentList(attachment));
    }

    @Operation(summary = "保存附件信息-关联业务", description = "比如关联了业务ID,业务类型")
    @PostMapping("/save")
    public R<?> save(@RequestBody List<Attachment> attachmentList) {
        attachmentService.saveAttachmentList(attachmentList);
        return R.ok();
    }

    @GetMapping("/aliyun/policy")
    @Operation(summary = "阿里云-根据文件名获取上传的签名(文件名中文请进行url编码)", description = "根据文件名获取上传的签名,前端直传OSS")
    public R<Map<String, String>> ossPolicy(@Schema(description = "文件名称") @Param("fileName") String fileName) {

        // 校验文件是否支持上传
        attachmentService.checkFileSuffix(fileName);

        // 生成文件名
        String generateFileName = attachmentService.generateFileName(fileName, StorageTypeEnum.OSS);

        // 文件路径
        String filePath = attachmentService.generateFilePath(generateFileName);

        OSSPutDTO policy = OssClientUtil.policy(filePath);
        policy.setFileKey(Base64.getEncoder().encodeToString(filePath.getBytes()));

        // 计算回调参数
        Map<String, Object> map = Map.of(
                "callbackUrl", fileProperties.getHost() + "/v1/attachment/oss-call",
                "callbackBody", "size=${size}&object=${object}&fileKey=${x:file_key}",
                "callbackBodyType", "application/x-www-form-urlencoded",
                "callbackSNI", fileProperties.getHost().contains("https"));
        String callbackBase64 = Base64.getEncoder().encodeToString(JSONUtil.toJsonStr(map).getBytes());

        Map<String, String> mapStr = JSONUtil.toMapStr(policy);
        mapStr.put("callback", callbackBase64);
        mapStr.put("x:file_key", policy.getFileKey());

        // 创建上传附件信息
        attachmentService.createAttachment(StorageTypeEnum.OSS, policy.getFileKey(), fileName, policy.getKey());

        return R.ok(mapStr);
    }

    @Operation(summary = "阿里云OSS回调", description = "阿里云OSS回调")
    @PostMapping("/oss-call")
    @Permission(permissionPublic = true)
    public R<OSSUrlDTO> ossCall(String fileKey, String object, String size, HttpServletRequest request) {
        log.info("收到阿里云回调: {}-{}-{}", fileKey, object, size);
        Attachment attachment = attachmentService.updateAttachment(fileKey, size);

        return R.ok(attachmentService.downloadUrl(attachment));
    }

    @GetMapping("/local/policy")
    @Operation(summary = "服务器-根据文件名获取上传的签名(文件名中文请进行url编码)", description = "根据文件名获取上传的签名,前端直传服务器")
    public R<OSSPutDTO> localPolicy(@Schema(description = "文件名称") @Param("fileName") String fileName) {

        // 校验文件是否支持上传
        attachmentService.checkFileSuffix(fileName);

        // 生成文件名
        String generateFileName = attachmentService.generateFileName(fileName, StorageTypeEnum.LOCAL);

        // 生成文件存储路径
        String filePath = attachmentService.generateFilePath(generateFileName);
        String fileKey = Base64.getEncoder().encodeToString(filePath.getBytes());
        // 创建上传附件信息
        attachmentService.createAttachment(StorageTypeEnum.LOCAL, fileKey, fileName, filePath);

        OSSPutDTO ossPutDTO = new OSSPutDTO();
        ossPutDTO.setKey(filePath);
        ossPutDTO.setFileKey(fileKey);
        ossPutDTO.setHost(fileProperties.getHost() + "/v1/attachment/upload");
        return R.ok(ossPutDTO);
    }


    @PostMapping("/upload")
    @Operation(summary = "服务器直传示例")
    public R<OSSUrlDTO> upload(@RequestPart(value = "file") MultipartFile multipartFile, @RequestPart("fileKey") String fileKey, HttpServletResponse httpServletResponse) throws IOException {
        Attachment attachment = attachmentService.uploadLocalAttachment(multipartFile, fileKey);
        return R.ok(attachmentService.downloadUrl(attachment));
    }

    @GetMapping("/policy-url")
    @Operation(summary = "根据File-key获取下载的地址", description = "根据File-key获取下载的地址(注意每次下载在超时时间内都可以下载)")
    public R<OSSUrlDTO> policyUrl(@Schema(description = "我们自己系统的fileKey") String fileKey) {
        return R.ok(attachmentService.selectDownloadByFileKey(fileKey));
    }

}
